package com.yilizhiyou.algorithm;

import com.yilizhiyou.model.UserPreference;
import com.yilizhiyou.entity.ScenicSpot;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 决策树推荐算法实现
 * 基于用户特征和景点属性进行决策树分类推荐
 * 
 * @author 伊犁智游开发团队
 * @since 1.0.0
 */
@Component
public class DecisionTreeRecommendation {
    
    /**
     * 决策树节点
     */
    private static class DecisionNode {
        String attribute;
        Object threshold;
        DecisionNode left;
        DecisionNode right;
        List<ScenicSpot> spots;
        boolean isLeaf;
        
        DecisionNode() {
            this.spots = new ArrayList<>();
            this.isLeaf = false;
        }
    }
    
    /**
     * 基于决策树进行推荐
     * 
     * @param userPreference 用户偏好
     * @param allSpots 所有景点
     * @param topN 返回前N个推荐
     * @return 推荐的景点列表
     */
    public List<ScenicSpot> recommendByDecisionTree(UserPreference userPreference, 
                                                   List<ScenicSpot> allSpots, 
                                                   int topN) {
        
        // 1. 构建决策树
        DecisionNode root = buildDecisionTree(allSpots, userPreference);
        
        // 2. 遍历决策树找到匹配的景点
        List<ScenicSpot> recommendedSpots = traverseDecisionTree(root, userPreference);
        
        // 3. 对推荐结果进行排序和过滤
        return recommendedSpots.stream()
            .distinct()
            .sorted((s1, s2) -> calculateSpotScore(s2, userPreference).compareTo(calculateSpotScore(s1, userPreference)))
            .limit(topN)
            .collect(Collectors.toList());
    }
    
    /**
     * 构建决策树
     */
    private DecisionNode buildDecisionTree(List<ScenicSpot> spots, UserPreference userPreference) {
        DecisionNode root = new DecisionNode();
        root.spots = new ArrayList<>(spots);
        
        buildTreeRecursive(root, userPreference, 0);
        
        return root;
    }
    
    /**
     * 递归构建决策树
     */
    private void buildTreeRecursive(DecisionNode node, UserPreference userPreference, int depth) {
        if (node.spots.size() <= 5 || depth >= 3) {
            node.isLeaf = true;
            return;
        }
        
        // 选择最佳分割属性
        String bestAttribute = selectBestAttribute(node.spots, userPreference);
        if (bestAttribute == null) {
            node.isLeaf = true;
            return;
        }
        
        node.attribute = bestAttribute;
        
        // 根据属性分割数据
        Map<String, List<ScenicSpot>> splits = splitByAttribute(node.spots, bestAttribute, userPreference);
        
        if (splits.size() >= 2) {
            node.left = new DecisionNode();
            node.right = new DecisionNode();
            
            List<ScenicSpot> leftSpots = splits.get("left");
            List<ScenicSpot> rightSpots = splits.get("right");
            
            if (leftSpots != null && !leftSpots.isEmpty()) {
                node.left.spots = leftSpots;
                buildTreeRecursive(node.left, userPreference, depth + 1);
            } else {
                node.left.isLeaf = true;
            }
            
            if (rightSpots != null && !rightSpots.isEmpty()) {
                node.right.spots = rightSpots;
                buildTreeRecursive(node.right, userPreference, depth + 1);
            } else {
                node.right.isLeaf = true;
            }
        } else {
            node.isLeaf = true;
        }
    }
    
    /**
     * 选择最佳分割属性
     */
    private String selectBestAttribute(List<ScenicSpot> spots, UserPreference userPreference) {
        double bestGain = 0.0;
        String bestAttribute = null;
        
        // 测试不同的属性
        String[] attributes = {"type", "price", "rating", "weather"};
        
        for (String attribute : attributes) {
            double gain = calculateInformationGain(spots, attribute, userPreference);
            if (gain > bestGain) {
                bestGain = gain;
                bestAttribute = attribute;
            }
        }
        
        return bestAttribute;
    }
    
    /**
     * 计算信息增益
     */
    private double calculateInformationGain(List<ScenicSpot> spots, String attribute, UserPreference userPreference) {
        double entropyBefore = calculateEntropy(spots, userPreference);
        
        Map<String, List<ScenicSpot>> splits = splitByAttribute(spots, attribute, userPreference);
        
        double entropyAfter = 0.0;
        for (List<ScenicSpot> split : splits.values()) {
            if (!split.isEmpty()) {
                double weight = (double) split.size() / spots.size();
                entropyAfter += weight * calculateEntropy(split, userPreference);
            }
        }
        
        return entropyBefore - entropyAfter;
    }
    
    /**
     * 计算熵
     */
    private double calculateEntropy(List<ScenicSpot> spots, UserPreference userPreference) {
        if (spots.isEmpty()) return 0.0;
        
        Map<String, Integer> classCounts = new HashMap<>();
        
        for (ScenicSpot spot : spots) {
            double score = calculateSpotScore(spot, userPreference);
            String className = score >= 3.0 ? "high" : "low";
            classCounts.put(className, classCounts.getOrDefault(className, 0) + 1);
        }
        
        double entropy = 0.0;
        for (int count : classCounts.values()) {
            double probability = (double) count / spots.size();
            if (probability > 0) {
                entropy -= probability * Math.log(probability) / Math.log(2);
            }
        }
        
        return entropy;
    }
    
    /**
     * 根据属性分割数据
     */
    private Map<String, List<ScenicSpot>> splitByAttribute(List<ScenicSpot> spots, String attribute, UserPreference userPreference) {
        Map<String, List<ScenicSpot>> splits = new HashMap<>();
        List<ScenicSpot> leftSpots = new ArrayList<>();
        List<ScenicSpot> rightSpots = new ArrayList<>();
        
        switch (attribute) {
            case "type":
                for (ScenicSpot spot : spots) {
                    if (isTypePreferred(spot.getType(), userPreference)) {
                        leftSpots.add(spot);
                    } else {
                        rightSpots.add(spot);
                    }
                }
                break;
                
            case "price":
                double priceThreshold = userPreference.getPricePreference() * 20; // 转换为实际价格
                for (ScenicSpot spot : spots) {
                    if (spot.getPrice() != null && spot.getPrice() <= priceThreshold) {
                        leftSpots.add(spot);
                    } else {
                        rightSpots.add(spot);
                    }
                }
                break;
                
            case "rating":
                double ratingThreshold = 4.0;
                for (ScenicSpot spot : spots) {
                    if (spot.getRating() != null && spot.getRating() >= ratingThreshold) {
                        leftSpots.add(spot);
                    } else {
                        rightSpots.add(spot);
                    }
                }
                break;
                
            case "weather":
                for (ScenicSpot spot : spots) {
                    if (isWeatherSuitable(spot.getWeather(), userPreference)) {
                        leftSpots.add(spot);
                    } else {
                        rightSpots.add(spot);
                    }
                }
                break;
        }
        
        splits.put("left", leftSpots);
        splits.put("right", rightSpots);
        
        return splits;
    }
    
    /**
     * 遍历决策树
     */
    private List<ScenicSpot> traverseDecisionTree(DecisionNode node, UserPreference userPreference) {
        List<ScenicSpot> result = new ArrayList<>();
        
        if (node.isLeaf) {
            return new ArrayList<>(node.spots);
        }
        
        if (node.left != null) {
            result.addAll(traverseDecisionTree(node.left, userPreference));
        }
        
        if (node.right != null) {
            result.addAll(traverseDecisionTree(node.right, userPreference));
        }
        
        return result;
    }
    
    /**
     * 判断景点类型是否被用户偏好
     */
    private boolean isTypePreferred(String spotType, UserPreference userPreference) {
        switch (spotType) {
            case "自然风光":
                return userPreference.getNatureInterest() >= 3.0;
            case "人文景观":
                return userPreference.getCulturalInterest() >= 3.0;
            default:
                return userPreference.getHistoricalInterest() >= 3.0;
        }
    }
    
    /**
     * 判断天气是否适合
     */
    private boolean isWeatherSuitable(String weather, UserPreference userPreference) {
        if (weather == null) return true;
        
        // 根据用户活动强度偏好判断天气适合度
        switch (weather) {
            case "晴":
                return userPreference.getActivityIntensity() >= 2.0;
            case "多云":
                return true; // 多云适合所有活动
            case "小雨":
                return userPreference.getActivityIntensity() <= 3.0;
            case "中雨":
            case "大雨":
                return userPreference.getActivityIntensity() <= 2.0;
            case "雪":
                return userPreference.getActivityIntensity() <= 2.0;
            case "雾":
                return userPreference.getActivityIntensity() <= 3.0;
            default:
                return true;
        }
    }
    
    /**
     * 计算景点分数
     */
    private Double calculateSpotScore(ScenicSpot spot, UserPreference userPreference) {
        double score = 3.0; // 基础分数
        
        // 根据景点类型调整分数
        String spotType = spot.getType();
        switch (spotType) {
            case "自然风光":
                score += (userPreference.getNatureInterest() - 3.0) * 0.5;
                break;
            case "人文景观":
                score += (userPreference.getCulturalInterest() - 3.0) * 0.5;
                break;
            default:
                score += (userPreference.getHistoricalInterest() - 3.0) * 0.3;
                break;
        }
        
        // 根据价格偏好调整分数
        if (spot.getPrice() != null) {
            double priceLevel = spot.getPrice() / 100.0;
            score -= Math.abs(priceLevel - userPreference.getPricePreference()) * 0.3;
        }
        
        // 根据评分调整分数
        if (spot.getRating() != null) {
            score += (spot.getRating() - 4.0) * 0.4;
        }
        
        // 根据天气调整分数
        if (spot.getWeather() != null) {
            if (isWeatherSuitable(spot.getWeather(), userPreference)) {
                score += 0.2;
            } else {
                score -= 0.3;
            }
        }
        
        return Math.max(1.0, Math.min(5.0, score));
    }
}
